/**
 * MySQL数据库连接配置模块
 * 使用mysql2/promise库提供Promise风格的MySQL操作接口
 * 这个模块负责创建数据库连接池并提供常用的数据库操作方法
 */

// 导入mysql2/promise库 - 这是MySQL的Node.js驱动程序，支持Promise异步操作
const mysql = require('mysql2/promise');

// 导入dotenv库，用于从.env文件加载环境变量
require('dotenv').config();

/**
 * 数据库连接配置对象
 * 这些配置参数从环境变量中读取，确保敏感信息不硬编码在代码中
 * 配置说明：
 * - host: MySQL服务器地址（如localhost或IP地址）
 * - port: MySQL服务器端口（默认3306）
 * - user: 数据库用户名
 * - password: 数据库密码
 * - database: 要连接的数据库名称
 * - charset: 字符集，utf8mb4支持所有Unicode字符（包括emoji）
 * - connectTimeout: 连接超时时间（毫秒）
 * - acquireTimeout: 获取连接超时时间（毫秒）
 * - timeout: 查询超时时间（毫秒）
 * - reconnect: 是否自动重连
 * - connectionLimit: 连接池最大连接数，控制并发连接数量
 * - queueLimit: 连接队列限制，0表示无限制
 */
const dbConfig = {
  host: process.env.DB_HOST,        // 从环境变量读取数据库主机地址
  port: process.env.DB_PORT,        // 从环境变量读取数据库端口
  user: process.env.DB_USER,        // 从环境变量读取数据库用户名
  password: process.env.DB_PASSWORD, // 从环境变量读取数据库密码
  database: process.env.DB_NAME,    // 从环境变量读取数据库名称
  charset: 'utf8mb4',               // 使用utf8mb4字符集，支持所有Unicode字符
  connectTimeout: 60000,            // 连接超时60秒
  acquireTimeout: 60000,            // 获取连接超时60秒
  timeout: 60000,                   // 查询超时60秒
  reconnect: true,                  // 启用自动重连
  connectionLimit: 10,              // 连接池最多10个连接
  queueLimit: 0                     // 无限制的等待队列
};

/**
 * 创建MySQL连接池
 * 连接池是管理数据库连接的重要机制，它可以：
 * 1. 复用连接，避免频繁创建和销毁连接的开销
 * 2. 控制并发连接数量，防止数据库过载
 * 3. 提供连接健康检查和管理
 */
const pool = mysql.createPool(dbConfig);

/**
 * 测试数据库连接函数
 * 这个函数用于验证数据库连接是否正常
 * 在实际应用中，可以在应用启动时调用此函数进行检查
 *
 * @returns {Promise<boolean>} 连接成功返回true，失败返回false
 */
async function testConnection() {
  try {
    // 从连接池获取一个连接
    const connection = await pool.getConnection();
    console.log('✅ MySQL数据库连接成功');
    
    // 释放连接回连接池，供其他请求使用
    connection.release();
    return true;
  } catch (error) {
    console.error('❌ MySQL数据库连接失败:', error.message);
    return false;
  }
}

/**
 * 通用SQL查询函数
 * 用于执行任意SQL查询语句，支持预处理语句（防止SQL注入）
 *
 * @param {string} sql - SQL查询语句，可以使用?作为参数占位符
 * @param {Array} params - 参数数组，对应SQL中的?占位符
 * @returns {Promise<Array>} 查询结果数组
 * @throws {Error} 如果查询失败则抛出错误
 *
 * 示例：
 * await query('SELECT * FROM users WHERE id = ?', [1])
 * await query('INSERT INTO users (name, email) VALUES (?, ?)', ['张三', 'zhangsan@example.com'])
 */
async function query(sql, params = []) {
  try {
    // 使用pool.execute()执行SQL，它支持预处理语句
    // [results]是解构赋值，因为execute返回[rows, fields]数组
    const [results] = await pool.execute(sql, params);
    return results;
  } catch (error) {
    console.error('数据库查询错误:', error);
    throw error; // 抛出错误让调用者处理
  }
}

/**
 * 获取单条记录函数
 * 专门用于查询单条记录的便捷函数，返回第一条记录或null
 *
 * @param {string} sql - SQL查询语句
 * @param {Array} params - 参数数组
 * @returns {Promise<Object|null>} 单条记录对象或null（无结果时）
 * @throws {Error} 如果查询失败则抛出错误
 *
 * 示例：
 * const user = await get('SELECT * FROM users WHERE id = ?', [1])
 * // 如果找到用户返回用户对象，否则返回null
 */
async function get(sql, params = []) {
  try {
    const [results] = await pool.execute(sql, params);
    // results[0]获取第一条记录，如果无结果则返回null
    return results[0] || null;
  } catch (error) {
    console.error('数据库查询错误:', error);
    throw error;
  }
}

/**
 * 插入数据函数
 * 专门用于执行INSERT语句，并返回新插入记录的ID
 *
 * @param {string} sql - INSERT SQL语句
 * @param {Array} params - 插入的参数值
 * @returns {Promise<number>} 新插入记录的自增ID
 * @throws {Error} 如果插入失败则抛出错误
 *
 * 示例：
 * const newUserId = await insert(
 *   'INSERT INTO users (name, email) VALUES (?, ?)',
 *   ['李四', 'lisi@example.com']
 * )
 */
async function insert(sql, params = []) {
  try {
    const [result] = await pool.execute(sql, params);
    // insertId是MySQL返回的自增ID字段
    return result.insertId;
  } catch (error) {
    console.error('数据库插入错误:', error);
    throw error;
  }
}

/**
 * 更新数据函数
 * 专门用于执行UPDATE语句，返回受影响的行数
 *
 * @param {string} sql - UPDATE SQL语句
 * @param {Array} params - 更新的参数值
 * @returns {Promise<number>} 受影响的行数
 * @throws {Error} 如果更新失败则抛出错误
 *
 * 示例：
 * const affectedRows = await update(
 *   'UPDATE users SET name = ? WHERE id = ?',
 *   ['王五', 1]
 * )
 * // 返回1表示成功更新了1行
 */
async function update(sql, params = []) {
  try {
    const [result] = await pool.execute(sql, params);
    // affectedRows表示被UPDATE语句影响的行数
    return result.affectedRows;
  } catch (error) {
    console.error('数据库更新错误:', error);
    throw error;
  }
}

/**
 * 删除数据函数
 * 专门用于执行DELETE语句，返回受影响的行数
 *
 * @param {string} sql - DELETE SQL语句
 * @param {Array} params - 删除的参数值
 * @returns {Promise<number>} 受影响的行数
 * @throws {Error} 如果删除失败则抛出错误
 *
 * 示例：
 * const deletedRows = await remove('DELETE FROM users WHERE id = ?', [1])
 * // 返回1表示成功删除了1行
 */
async function remove(sql, params = []) {
  try {
    const [result] = await pool.execute(sql, params);
    // affectedRows表示被DELETE语句影响的行数
    return result.affectedRows;
  } catch (error) {
    console.error('数据库删除错误:', error);
    throw error;
  }
}

/**
 * 开始事务函数
 * 事务用于确保一组SQL操作要么全部成功，要么全部失败
 * 在需要保证数据一致性的操作中使用（如转账、订单处理等）
 *
 * @returns {Promise<Object>} 事务连接对象
 *
 * 示例：
 * const connection = await beginTransaction()
 * try {
 *   // 执行多个SQL操作
 *   await connection.execute('UPDATE account SET balance = balance - ? WHERE id = ?', [100, 1])
 *   await connection.execute('UPDATE account SET balance = balance + ? WHERE id = ?', [100, 2])
 *   await commitTransaction(connection) // 提交事务
 * } catch (error) {
 *   await rollbackTransaction(connection) // 回滚事务
 * }
 */
async function beginTransaction() {
  // 从事务池获取一个专用连接用于事务操作
  const connection = await pool.getConnection();
  // 开始事务
  await connection.beginTransaction();
  return connection;
}

/**
 * 提交事务函数
 * 当事务中的所有操作都成功时调用此函数提交事务
 *
 * @param {Object} connection - 事务连接对象
 *
 * 注意：提交后事务连接会被释放回连接池
 */
async function commitTransaction(connection) {
  // 提交事务，使所有修改永久生效
  await connection.commit();
  // 释放连接回连接池
  connection.release();
}

/**
 * 回滚事务函数
 * 当事务中的任何操作失败时调用此函数回滚事务
 * 回滚会撤销事务中的所有修改，保持数据一致性
 *
 * @param {Object} connection - 事务连接对象
 *
 * 注意：回滚后事务连接会被释放回连接池
 */
async function rollbackTransaction(connection) {
  // 回滚事务，撤销所有未提交的修改
  await connection.rollback();
  // 释放连接回连接池
  connection.release();
}

/**
 * 模块导出
 * 将这些函数和连接池导出，供其他模块使用
 */
module.exports = {
  pool,                    // 数据库连接池，可直接使用
  testConnection,          // 测试连接函数
  query,                   // 通用查询函数
  get,                     // 获取单条记录函数
  insert,                  // 插入数据函数
  update,                  // 更新数据函数
  remove,                  // 删除数据函数
  beginTransaction,        // 开始事务函数
  commitTransaction,       // 提交事务函数
  rollbackTransaction      // 回滚事务函数
};